package alipay

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"log"
	"net/url"
	"sort"
	"strings"
	"time"
)

type RequestParam struct {
	BizToken      string   `json:"biz_token,omitempty"`
	PrincipalTag  string   `json:"principal_tag,omitempty"`
	AlipayPid     string   `json:"alipay_pid,omitempty"`
	Uuid          string   `json:"uuid,omitempty"`
	CrowdId       string   `json:"crowd_id,omitempty"`
	CrowdName     string   `json:"crowd_name,omitempty"`
	OpenTime      string   `json:"open_time,omitempty"`
	ExpiryTime    string   `json:"expiry_time,omitempty"`
	BehaviorTypes []string `json:"behavior_types"`
	IfEnd         bool     `json:"if_end"`
	DeviceIds     []string `json:"device_ids,omitempty"`
	OperateType   string   `json:"operate_type,omitempty"`
	DataType      string   `json:"data_type"`
}

type Response struct {
	AlipayDataDataserviceAdDmpCreateResponse ResponseData `json:"alipay_data_dataservice_ad_dmp_create_response"`
	Sign                                     string       `json:"sign"`
}

type ResponseData struct {
	Code      string `json:"code"`
	Msg       string `json:"msg"`
	Status    string `json:"status"`
	CrowdId   string `json:"crowd_id"`
	CrowdName string `json:"crowd_name"`
}

func Send(crowdName, operateType string, deviceIds []string, ifEnd bool, uuid string, crowdId string) (error, ResponseData) {
	defer func() {
		if err := recover(); err != nil {
			log.Println("报错", err)
		}
	}()
	timestamp := time.Now().Format("2006-01-02 15:04:05")
	publicParam := map[string]string{
		// 公共参数
		"app_id":    "2021004109660346",
		"method":    "alipay.data.dataservice.ad.dmp.create",
		"format":    "json",
		"charset":   "utf-8",
		"sign_type": "RSA2",
		"version":   "1.0",
		"timestamp": timestamp,
	}

	// 业务参数
	requestParam := RequestParam{
		BizToken: "cb8809d9ff2b4566be8399db346d0a61",
		//PrincipalTag: "023693762379433eba62a63e7f62c2a0",
		PrincipalTag: "8cdd53f0ada347dbbf99d89563b48bcd",
		//AlipayPid:    "2088741458840476",
		AlipayPid:     "2088741503893334",
		Uuid:          uuid,
		CrowdId:       crowdId,
		CrowdName:     crowdName,
		IfEnd:         ifEnd,
		OpenTime:      time.Now().Format("2006-01-02 15:04:05"),
		ExpiryTime:    time.Now().AddDate(0, 2, 0).Format("2006-01-02 00:00:00"),
		BehaviorTypes: []string{"APP_ORDER"},
		DataType:      "OAID_MD5",
		DeviceIds:     deviceIds,
		OperateType:   operateType,
	}

	bizContent, err := json.Marshal(requestParam)

	data := map[string]interface{}{
		"biz_content": string(bizContent),
	}

	publicParam["biz_content"] = string(bizContent)
	parseParam := getSignContent(publicParam)

	sign1, err := sign([]byte(parseParam))
	if err != nil {
		log.Println(err.Error())
		return err, ResponseData{}
	}
	publicParam["sign"] = sign1
	delete(publicParam, "biz_content")

	query := url.Values{}
	for k, v := range publicParam {
		query.Set(k, v)
	}
	res := FromUrlencodedPost("https://openapi.alipay.com/gateway.do?"+query.Encode(), data)
	if res.Err != nil {
		log.Println(res.Err)
		return err, ResponseData{}
	}
	body, _ := res.Body()
	var resData Response
	_ = json.Unmarshal(body, &resData)
	if resData.AlipayDataDataserviceAdDmpCreateResponse.Code != "10000" {
		return fmt.Errorf("返回错误"), ResponseData{}
	}
	if ifEnd {
		log.Println("result:", string(body))
	}
	return nil, resData.AlipayDataDataserviceAdDmpCreateResponse
}

const PrivateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCBUcvQ3uy4EmdH
3C4HiQV6JsNGkIzVpHbT1sGmq39i/5lppvt7KtY5yfj+jftllLtxdrZD+GdApzp0
KGYGaHXcCRCqiTR3m2uM8BsSYmwioKT22woLxUQ73Pu45t8reNCZ2dJ+V5ze+L3M
BK5ilIysoNWa23vvHIRUFfq4XaQqqaZJVxYh7EgtpprJDrvrEPB5tXpv5CQPkz4Z
zelFfajfkR504acHV43Jt1M1G1ANN6kIwmb+m8OafHE8gJ4RnyAXGWd8Rldg6Z8W
o1TpLuGufNZ9YKPQ3cpWmLZxmFnopFK4rR+VbPMybCmTe+yOghUJhG4ryCwbPE1K
Y/9uBzJ5AgMBAAECggEAV2qAeP/ISqF+GVBX+RYBktHTHMcnvF9BR++a1dyF7tjS
zl1nD5okV8oUu5Ykg0Un9AoIKSgoO8cPN2f7l4dTbFrgXjcDNXrlF89SrXnEmR+4
g3oLb2t4tXKq4hGGeC9h6y4YIPl+fCbuvZPIgbDi6uqJmfWV8Et5lBGLescp6v/u
9X5Ncw6OFg/WWM0PXkkAiusuecY4ZLgtSFM1I27/E5IwcbmLzvU5r/jdt9xyCZzZ
F3r3KHHcrG5jXQoxV/SdoLY74fZrjATSvUF+5b6agxes40BGjjhlcYF6L6skxVtd
i0JeXCgAuLhbv+GUUuyr/nJ1TTpzeVN4RvpcSh8D5QKBgQC5SbZSSqO/QQmPt0Q5
g/xoTv/Ofm/umJz9iX5fkAZI5medZsgDsUm5ObI+rFUx/72b2/5PjomS3YMBJBXF
dbw1G7scHXCs1i7W3r7NcoJMSXhRHD3Cy+TbkYr8Cn2YnDKYy1DgFLxA3+GJ+kcc
63hDBzYoomTC28AWKZV/iQJVlwKBgQCyrBKuDC5bU8BRzWGLhPWfdgY/M2bTE+an
1jw9hnNPPyDk0vA5oszSktYtO9pz+xh1IYJDZMMaY/QypIpoKsZ7ywHyuY1KmisE
NjoiGU+2oR7NbBvkyj6G5K4+VZNyB6v2zrSB8U1ufoW4bY1uaH01e2UO7KlD4nAN
tzuX2xtabwKBgGxoPEwZb4TOwJFdYIVmO5+r2O8v+r1WfUO51j6DPaoZZDYrlIrU
h5Xfs/IHUjFWt9Eg/cH+W2+aOrPIMRS+tcc82lSwxOPazLzaxjfd725C7ipGo7v9
H4F3viGI4cMQYGoVStRQTXSHiKX50PGSs2JELePIX7vCZriQy+86cisxAoGAbuvg
lOi/BreR17b3bdRXYG23JxlUJopeUafwsgl8IKCCXUyPElj9a2T5Tg+waPxjjKQG
6Kvva7zN53q66jxUCJpZVUTYcI4RUoYPxQeNs0WHh+Qr1PexpbDmc5BQ/yPAIHLn
IRybA5PMlFh+yyJXSRKYBJHr61byVbGBb5749kUCgYBT8J2JXnQO3U38M1picki7
wTBpyyhKf9JGV3GAiug50XNerXXb99osa7OC2ddbtjTOFg/9wlRX6SaFSyMPB/4t
8+4wUYQWgWLd/O7Kj/mXvRjHfSyeLTDC+X5HSZT5YDVPkQn9MEDpr65c5bx1OHXj
Z43+VfXA5Ppwv8UtbyOJEg==
-----END RSA PRIVATE KEY-----`

func sign(data []byte) (string, error) {
	var privateKeyBytes []byte
	var err error

	privateKeyBytes = []byte(PrivateKey)

	var privateKey *rsa.PrivateKey
	privateKey, err = parseRSAPrivateKey(privateKeyBytes)

	if err != nil {
		return "", err
	}

	hash := sha256.Sum256(data)
	signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
	if err != nil {
		return "", err
	}

	return base64.StdEncoding.EncodeToString(signature), nil
}

func parseRSAPrivateKey(pemBytes []byte) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, fmt.Errorf("failed to parse PEM block containing the private key")
	}
	parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	privateKeyRSA, ok := parsedKey.(*rsa.PrivateKey)
	if !ok {
		return nil, fmt.Errorf("parsed key is not an RSA private key")
	}

	return privateKeyRSA, nil
}

func getSignContent(params map[string]string) string {
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var stringToBeSigned strings.Builder
	i := 0
	for _, k := range keys {
		v := params[k]
		// 转换成目标字符集
		if i == 0 {
			stringToBeSigned.WriteString(k + "=" + v)
		} else {
			stringToBeSigned.WriteString("&" + k + "=" + v)
		}
		i++
	}

	return stringToBeSigned.String()
}
